/**
 * Copyright (C) 2012 Oleg Anastassov <o.anastasov@gmail.com>
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package kz.kstu.ent.desktop.components.validation;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Oleg Anastassov
 */
public class ComponentValidator {

    private Map<JComponent, Validators> validatorMap = new HashMap<JComponent, Validators>();
    private Map<JComponent, Border> borderMap = new HashMap<JComponent, Border>();
    private Map<JComponent, Popup> popupMap = new HashMap<JComponent, Popup>();

    public void addValidator(JComponent component, Validator validator) {
        Validators validators = validatorMap.get(component);
        if (validators == null) {
            validators = new Validators();
            validatorMap.put(component, validators);
        }
        validators.addValidator(validator);

        // сохраняем оригинальный бордюр компонента
        borderMap.put(component, component.getBorder());
    }

    public boolean isValid() {
        boolean isValid = true;
        for (JComponent component : validatorMap.keySet()) {
            isValid = validate(component) && isValid;
        }
        return isValid;
    }

    public boolean validate(JComponent component) {
        Validators validators = validatorMap.get(component);
        if (component == null) {
            return true;
        }
        boolean isValid = true;
        for (Validator validator : validators.getValidators()) {
            Object value = getComponentValue(component);
            if (!validator.isValid(value)) {
                isValid = false;
                addError(component, validator.getErrorText());
            } else {
                removeError(component);
            }
        }
        return isValid;
    }

    private Object getComponentValue(JComponent component) {
        if (component instanceof JTextComponent) {
            return ((JTextComponent) component).getText();
        } else if (component instanceof JComboBox) {
            return ((JComboBox) component).getSelectedItem();
        }
        return null;
    }

    private void addError(final JComponent component, String errorText) {
//        component.setBorder(BorderFactory.createLineBorder(Color.red));
        Border redBorder = BorderFactory.createLineBorder(Color.red);
        component.setBorder(redBorder);

        component.setToolTipText(errorText);


        JToolTip toolTip = new JToolTip();
        toolTip.setTipText(errorText);
        toolTip.setComponent(component);

        PopupFactory popupFactory = PopupFactory.getSharedInstance();
        Point location = component.getLocationOnScreen();
        Dimension size = component.getSize();
        int x = location.x + size.width - toolTip.getPreferredSize().width;
        int y = (location.y + size.height / 2);
        Popup popup = popupFactory.getPopup(component, toolTip, x, y);
        popupMap.put(component, popup);

        popup.show();

        component.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                Popup popup = popupMap.get(component);
                popup.hide();
            }
        });

        if (component instanceof JTextComponent) {
            ((JTextComponent) component).getDocument().addDocumentListener(new DocumentListener() {
                @Override
                public void insertUpdate(DocumentEvent e) {
                    updated();
                }

                @Override
                public void removeUpdate(DocumentEvent e) {
                    updated();
                }

                @Override
                public void changedUpdate(DocumentEvent e) {
                    updated();
                }

                private void updated() {
                    validate(component);
                }
            });
        }
    }

    private void removeError(JComponent component) {
        // восстанавливаем сохраненный ранее бордюр
        Border originalBorder = borderMap.get(component);
        component.setBorder(originalBorder);
        Popup popup = popupMap.get(component);
        if (popup != null) {
            popup.hide();
            popupMap.remove(component);
        }
    }
}
